#ifndef OMG_DDS_DOMAIN_PARTICIPANT_HPP_
#define OMG_DDS_DOMAIN_PARTICIPANT_HPP_

/* Copyright (c) 2009-2010, Real-Time Innovations, Inc.
 * Copyright (c) 2010, Object Management Group, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - Neither the names of the above copyright holders nor the names of their
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL A COPYRIGHT HOLDER OR CONTRIBUTOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */


/**
 * The <code>DomainParticipant</code> object plays several roles:
 * <ul>
 *   <li>
 *     It represents the participation of the application on a communication 
 *     plane that isolates applications running on the same set of physical 
 *     computers from each other. A domain establishes a “virtual network” 
 *     linking all applications that share the same domainId and isolating
 *     them from applications running on different domains. In this way, 
 *     several independent distributed applications can coexist in the same
 *     physical network without interfering, or even being aware of each 
 *     other.
 *   </li>
 *   <li>
 *     It provides access to the information available in a domain and
 *     currently discovered by the discovery service. This class also
 *     provides administration services in the domain, offering operations
 *     that allow the application to "ignore" locally any information about a
 *     given participant (ignore_participant), publication
 *     (ignore_publication), subscription (ignore_subscription), or topic
 *     (ignore_topic).
 *   </li>
 * </ul>
 */
template <typename DELEGATE>
class tdds::domain::DomainParticipant : public dds::core::Entity
{
    OMG_DDS_REFERENCE_TYPE(DomainParticipant)

public:
    typedef dds::core::StatusCondition<DomainParticipant> StatusConditionType;

public:
    /**
     * This operation retrieves the domain_id used to create the 
     * <code>DomainParticipant</code>. The domain_id identifies the DDS domain
     * to which the <code>DomainParticipant</code> belongs. 
     * As described in the introduction to Section 7.1.2.2.1, 
     * “DomainParticipant Class,” on page 22 each DDS domain represents a
     * separate data “communication plane” isolated from other domains.
     *
     * @return the domain id
     */ 
    dds::domain::DomainId get_domain_id();

    /**
     * This operation manually asserts the liveliness of the DataWriter. 
     * This is used in combination with the LIVELINESS QoS policy 
     * (see Section 7.1.3, “Supported QoS,” on page 96) to indicate to the
     * Service that the entity remains active.
     * This operation need only be used if the LIVELINESS setting is either
     * MANUAL_BY_PARTICIPANT or MANUAL_BY_TOPIC. Otherwise, it has no effect.
     * 
     * <b>Note</b> – Writing data via the write operation on a DataWriter 
     * asserts liveliness on the DataWriter itself and its DomainParticipant. 
     * Consequently the use of assert_liveliness is only needed if the 
     * application is not writing data regularly.
     */
    void assert_liveliness();

    /**
     * This operation checks whether or not the given a_handle represents 
     * an Entity that was created from the DomainParticipant. 
     * The containment applies recursively. That is, it applies both to 
     * entities (TopicDescription, Publisher, or Subscriber) created directly
     * using the DomainParticipant as well as entities created using a 
     * contained Publisher, or Subscriber as the factory, and so forth.
     *
     * @param handle the instance handle for which the containement 
     *               relationship has to be checked.
     *
     * @return true if the handle belongs to an <code>Entity</code> belonging
     *              to this <code>DomainParticipant</code>
     */
    bool contains_entity(const dds::core::InstanceHandle& a_handle);

    /**
     * This operation returns the current value of the time that the service
     * uses to time-stamp data-writes and to set the reception-timestamp 
     * for the data-updates it receives.
     */
    dds::core::Time get_current_time();

public:
    /**
     * This operation allows access to the built-in Subscriber. 
     * Information on a Domain is accessed by means of  several built-in 
     * Topic objects as well as corresponding DataReader objects to access them. 
     * All these DataReader objects belong to a single built-in Subscriber.
     *
     * @return the built-in <code>Subscriber</code>
     */
    dds::sub::Subscriber get_builtin_subscriber();

    /**
     * The operation <code>find_topic</code> gives access to an existing 
     * (or ready to exist) enabled Topic, based on its name. 
     * The operation takes as arguments the name of the Topic and a timeout.
     * If a <code>Topic</code> of the same name already exists, 
     * it gives access to it, otherwise it waits (blocks the caller) until 
     * another mechanism creates it (or the specified timeout occurs). 
     * This other mechanism can be another thread, a configuration tool, 
     * or some other middleware service. Note that the Topic is a local object
     * that acts as a ‘proxy’ to designate the global concept of topic. 
     * Middleware implementations could choose to propagate topics and make 
     * remotely created topics locally available.
     *
     * @param topic_name the <code>Topic</code> name.
     * @param timeout the maximum time for which the call will block
     * @return the <code>Topic</code> matching the name, or a null reference.
     */
    template <typename T>
    dds::topic::Topic<T>
    find_topic(const std::string &topic_name, 
               const dds::core::Duration &timeout);

    /**
     * The operation <code>lookup_topicdescription</code> gives access to an 
     * existing locally-created <code>TopicDescription</code>, based on its 
     * name. The operation takes as argument the name of the 
     * <code>TopicDescription</code>.
     * If a <code>TopicDescription</code> of the same name already exists, 
     * it gives access to it, otherwise it returns a ‘nil’ value. 
     * The operation never blocks. The operation 
     * <code>lookup_topicdescription</code> may be used to locate any 
     * locally-created <code>Topic</code>, <code>ContentFilteredTopic</code>, 
     * and <code>MultiTopic</code> object.
     * Unlike <code>find_topic</code>, the operation 
     * <code>lookup_topicdescription</code> searches only among the locally 
     * created topics. Therefore, it should never create a new 
     * <code>TopicDescription</code>. 
     *
     * @param name the <code>Topic</code> name
     * @return the <code>TopicDescription</code> 
     */
    template <typename T>
    dds::topic::TopicDescription<T>
    lookup_topicdescription(const std::string& name) const;

    /**
     * This operation retrieves the list of DomainParticipants that have been 
     * discovered in the domain and that the application has not indicated 
     * should be “ignored” by means of the DomainParticipant 
     * <code<ignore_participant</code operation.
     * The operation may fail if the infrastructure does not locally maintain
     * the connectivity information. In this case the operation will 
     * raise UNSUPPORTED exception.
     *
     * @return the handles for the discovered participants.
     */
    dds::core::InstanceHandleSeq&
    get_discovered_participants(dds::core::InstanceHandleSeq& topic_handles) const;

    dds::core::InstanceHandleSeq
    get_discovered_participants() const;

    /**
     * This operation retrieves information on a DomainParticipant that has 
     * been discovered on the network. The participant must be in the same 
     * domain as the <code>DiscoveryService</code> on which this operation 
     * is invoked and must not have been “ignored” by means of the 
     * <code>ignore_participant</code> operation.
     * The participant_handle must correspond to such a DomainParticipant. 
     * Otherwise, the operation will fail and raise PRECONDITION_NOT_MET.
     * Use the operation get_discovered_participants to find the 
     * DomainParticipants that are currently discovered. The operation may 
     * also fail if the infrastructure does not hold the information necessary
     * to fill in the participant_data. In
     * this case the operation will return UNSUPPORTED.
     *
     * @param participant_handle the <code>DomainParticipant</code> handle
     * @return the <code>ParticipantBuiltinTopicData</code>
     */
    dds::domain::discovery::ParticipantBuiltinTopicData
    get_discovered_participant_data(
        const dds::core::InstanceHandle& participant_handle);

    /**
     * This operation retrieves the list of Topics that have been discovered 
     * in the domain and that the application has not indicated should be
     * “ignored” by means of the <code>ignore_topic</code> operation.
     */
    dds::core::InstanceHandleSeq&
    get_discovered_topics(dds::core::InstanceHandleSeq& topic_handles) const;

    dds::core::InstanceHandleSeq
    get_discovered_topics() const;

    /**
     * This operation retrieves information on a Topic that has been 
     * discovered on the network. The topic must have been created by a 
     * participant in the same domain as the domain of this 
     * <code>DiscoveryService</code> instance and must not have been “ignored” 
     * by means of the <code>ignore_topic operation</code>.
     * The topic_handle must correspond to such a topic. Otherwise, the 
     * operation will fail and return PRECONDITION_NOT_MET.
     * Use the operation get_discovered_topics to find the topics that are 
     * currently discovered. The operation may also fail if the infrastructure
     * does not hold the information necessary to fill in the topic_data. 
     * In this case the operation will raise an UNSUPPORTED exception.
     * The operation may fail if the infrastructure does not locally maintain 
     * the connectivity information. 
     * In this case the operation will raise UNSUPPORTED exception.
     *
     * @param topic_handle the Topic handle.
     * @return the discovered Topic data.
     */
    dds::domain::discovery::TopicBuiltinTopicData
    get_discovered_topic_data(const dds::core::InstanceHandle& topic_handle);

    /**
     * This operation allows an application to instruct the Service to locally
     * ignore a remote domain participant. From that point onwards the Service
     * will locally behave as if the remote participant did not exist.
     * This means it will ignore any Topic, publication, or subscription that 
     * originates on that domain participant.
     * This operation can be used, in conjunction with the discovery of remote
     * participants offered by means of the “DCPSParticipant” built-in Topic, 
     * to provide, for example, access control. Application data can be 
     * associated with a DomainParticipant by means of the USER_DATA QoS 
     * policy. This application data is propagated as a field in the built-in 
     * topic and can be used by an application to implement its own access 
     * control policy. See Section 7.1.5, “Built-in Topics,” on page 134 for 
     * more details on the built-in topics.
     * The domain participant to ignore is identified by the handle argument. 
     * This handle is the one that appears in the Sample retrieved when 
     * reading the data-samples available for the built-in DataReader to the 
     * “DCPSParticipant” topic. The built-in DataReader is read with the same 
     * read/take operations used for any DataReader. 
     * These data-accessing operations are described in Section 7.1.2.5, 
     * “Subscription Module,” on page 61.
     *
     * @param handle the participant handle.
     */
    void ignore_participant(const dds::core::InstanceHandle &handle);

    /**
     * This operation allows an application to instruct the Service to locally
     * ignore a Topic. This means it will locally ignore any publication or 
     * subscription to the Topic.
     * This operation can be used to save local resources when the application 
     * knows that it will never publish or subscribe to data under certain 
     * topics.
     * The Topic to ignore is identified by the handle argument. This handle 
     * is the one that appears in the Sample retrieved when reading 
     * the data-samples from the built-in DataReader to the “DCPSTopic” topic.
     * The ignore_topic operation is not required to be reversible. 
     * The Service offers no means to reverse it. Possible exceptions raised
     * in addition to the standard ones: OUT_OF_RESOURCES.
     *
     * @param handle the handle for the <code>Topic</code> to ignore.
     */
    void ignore_topic(const dds::core::InstanceHandle &handle);

    /**
     * This operation allows an application to instruct the Service to locally 
     * ignore a remote publication; a publication is defined by the association 
     * of a topic name, and user data and partition set on the Publisher 
     * (see the “DCPSPublication” built-in Topic in Section 7.1.5, 
     * “Built-in Topics,” on page 134). After this call, any data written 
     * related to that publication will be ignored.
     * The <code>DataWriter</code> to ignore is identified by the handle 
     * argument. This handle is the one that appears in the Sample 
     * retrieved when reading the data-samples from the built-in DataReader 
     * to the “DCPSPublication” topic.
     * The ignore_publication operation is not required to be reversible. 
     * The Service offers no means to reverse it. Possible error exceptions 
     * raised in addition to the standard ones: OUT_OF_RESOURCES.
     *
     * @param handle the handle of the <code>DataWriter</code> to be ignored.
     */
    void ignore_publication(const dds::core::InstanceHandle &handle);

    /**
     * This operation allows an application to instruct the Service to locally
     * ignore a remote subscription; a subscription is defined by the 
     * association of a topic name, and user data and partition set on the 
     * Subscriber (see the “DCPSSubscription” built-in Topic in Section 7.1.5,
     * “Built-in Topics,” on page 134). After this call, any data received 
     * related to that subscription will be ignored.
     * The DataReader to ignore is identified by the handle argument. 
     * This handle is the one that appears in the Sample retrieved when 
     * reading the data-samples from the built-in DataReader to the 
     * “DCPSSubscription” topic.
     * The ignore_subscription operation is not required to be reversible. 
     * The Service offers no means to reverse it. Possible error codes 
     * returned in addition to the standard ones: OUT_OF_RESOURCES.
     *
     * @param the handle for the subscription to ignore.
     */
    void ignore_subscription(const dds::core::InstanceHandle &handle);

public:
    const dds::pub::PublisherQos get_default_publisher_qos() const;
    void set_default_publisher_qos(const dds::pub::PublisherQos& qos);
    void set_default_publisher_qos(const std::string& qos_library_name,
                                   const std::string& qos_profile_name);

    const dds::sub::SubscriberQos get_default_subscriber_qos() const;
    void set_default_subscriber_qos(const dds::sub::SubscriberQos& qos);
    void set_default_subscriber_qos(const std::string& qos_library_name,
                                    const std::string& qos_profile_name);

    const dds::topic::TopicQos get_default_topic_qos() const;
    void set_default_topic_qos(const dds::topic::TopicQos& qos);
    void set_default_topic_qos(const std::string& qos_library_name,
                               const std::string& qos_profile_name);

public:
    /**
     * This operation creates a Publisher with default QoS.
     */
    dds::pub::Publisher create_publisher();

    /**
     * This operation creates a Publisher with the desired QoS policies
     * and attaches to it the specified PublisherListener.
     * If the specified QoS policies are not consistent, the operation
     * will fail and an exception will be thrown.
     */
    dds::pub::Publisher create_publisher(
        const dds::pub::PublisherQos& qos,
        const dds::pub::PublisherListener *a_listener   = NULL,
        const dds::core::StatusMask& mask               = dds::core::StatusMask::all());

    /**
     * This operation creates a Publisher with the desired QoS policies
     * and attaches to it the specified PublisherListener.
     * If the specified QoS policies are not consistent, the operation
     * will fail and an exception will be thrown.
     */
    dds::pub::Publisher create_publisher(
        const std::string& qos_library_name,
        const std::string& qos_profile_name,
        const dds::pub::PublisherListener* a_listener   = NULL,
        const dds::core::StatusMask& mask               = dds::core::StatusMask::all());
    
    dds::sub::Subscriber create_subscriber();
    dds::sub::Subscriber create_subscriber(
        const dds::sub::SubscriberQos& qos,
        const dds::sub::SubscriberListener *a_listener  = NULL,
        const dds::core::StatusMask& a_mask             = dds::core::StatusMask::all());
    dds::sub::Subscriber create_subscriber(
        const std::string& qos_library_name,
        const std::string& qos_profile_name,
        const dds::sub::SubscriberListener* a_listener  = NULL,
        const dds::core::StatusMask& a_mask             = dds::core::StatusMask::all());
    
    template <typename T>
    dds::topic::Topic<T> create_topic(
        const std::string &topic_name, 
        dds::type::TypeSupport<T> type = dds::type::TypeSupport<T>());
    template <typename T>
    dds::topic::Topic<T> create_topic(
        const std::string &topic_name, 
        dds::type::TypeSupport<T> type,
        const dds::topic::TopicQos& qos,
        dds::topic::TopicListener<T>* a_listener    = NULL,
        const dds::core::StatusMask& mask           = dds::core::StatusMask::all());
    template <typename T>
    dds::topic::Topic<T> create_topic(
        const std::string &topic_name, 
        dds::type::TypeSupport<T> type,
        const std::string& qos_library_name,
        const std::string& qos_profile_name,
        dds::topic::TopicListener<T>* a_listener    = NULL,
        const dds::core::StatusMask& mask           = dds::core::StatusMask::all());

#ifdef OMG_DDS_CONTENT_SUBSCRIPTION_SUPPORT
    template <typename T>
    dds::topic::ContentFilteredTopic<T> create_contentfilteredtopic(
        const std::string& name,
        dds::topic::Topic<T>& related_topic,
        const std::string& filter_expression,
        const dds::core::StringSeq& expression_parameters);
#endif  // OMG_DDS_CONTENT_SUBSCRIPTION_SUPPORT
    
#ifdef OMG_DDS_CONTENT_SUBSCRIPTION_SUPPORT
    template <typename T>
    dds::topic::MultiTopic<T> create_multitopic(
        const std::string& name,
        const std::string& type_name, 
        const std::string& subscription_expression,
        const dds::core::StringSeq& expression_parameters);
#endif  // OMG_DDS_CONTENT_SUBSCRIPTION_SUPPORT

    // --- Entity methods: --- //
public:
    /**
     * This method closes the entity and releases all resources associated with
     * DDS, such as threads, sockets, buffers, etc. Any attempt to invoke
     * methods on a closed entity will raise an AlreadyClosedError.
     */
    virtual void close();

    StatusConditionType get_statuscondition();

    using dds::core::Entity::set_qos;
    void set_qos(const dds::domain::DomainParticipantQos& qos);
    const dds::domain::DomainParticipantQos get_qos() const;
    
    void set_listener(dds::domain::DomainParticipantListener* l,
                      const dds::core::StatusMask& mask = dds::core::StatusMask::all());
    
    dds::domain::DomainParticipantListener* get_listener() const;

    // --- State: --- //
private:
    friend class OMG_DDS_DOMAIN_PARTICIPANT_DELEGATE;
    DELEGATE impl_;
};


#endif // !defined(OMG_DDS_DOMAIN_PARTICIPANT_HPP_)
